/*
 * @Author: andy.dell andy@long.com
 * @Date: 2024-01-07 21:37:44
 * @LastEditors: andy.dell andy@long.com
 * @LastEditTime: 2024-01-07 19:20:56
 * @FilePath: \wasm\logic\snake\src\service\move.go
 * @Description:
 */
package service

import (
	"fmt"

	"com.game.snake/consts"
	"com.game.snake/model"
	"com.game.snake/utils"
)

func (g *Game) move() {
	headPos := g.Snake.GetHeadPos()
	nhead := headPos
	switch g.Direction {
	case consts.DirectionUp:
		nhead.Row = nhead.Row - 1
	case consts.DirectionDown:
		nhead.Row = nhead.Row + 1
	case consts.DirectionLeft:
		nhead.Col = nhead.Col - 1
	case consts.DirectionRight:
		nhead.Col = nhead.Col + 1
	default:
	}
	// 撞到边框
	if g.IsHitWall(nhead) {
		g.IsDead = true
		fmt.Println("撞墙而死！！！")
		return
	}
	// 空格子：正常移动
	obj := g.World[nhead.Row][nhead.Col]
	if obj == nil {
		tailIndex := g.Snake.Body[len(g.Snake.Body)-1]
		row, col := utils.Index2Pos(g.Set.Width, tailIndex)
		g.World[row][col] = nil                              // 移除蛇尾
		g.World[nhead.Row][nhead.Col] = model.NewSnakeBody() // 添加蛇头
		// 调整snake
		index := utils.Pos2Index(g.Set.Width, nhead.Row, nhead.Col) //
		delete(g.Snake.Dict, tailIndex)                             // 移除尾巴
		//
		withNewHeadSlice := append([]int{index}, g.Snake.Body...)
		g.Snake.Body = withNewHeadSlice[:len(withNewHeadSlice)-1] //添加蛇头，移除蛇尾
		g.Snake.Dict[index] = nhead
		// 如果上一步吃了食物，这里必须移除，否则地图上多一个固定位置是蛇的点，除非每次重新设置 蛇的身体
		if g.lastFood != nil {
			// 如果不是蛇的身体就置空
			index := utils.Pos2Index(g.Set.Width, g.lastFood.Row, g.lastFood.Col) //
			if _, exists := g.Snake.Dict[index]; !exists {
				g.World[g.lastFood.Row][g.lastFood.Col] = nil
				g.lastFood = nil
			}
		}
		return
	}
	// 吃到食物了：变长，添加蛇头位置，不需要移除蛇尾
	if obj.IsFood() {
		g.IsEated = true
		g.Score++
		g.Level = g.Score / 5
		// 变更映射到地图
		index := utils.Pos2Index(g.Set.Width, nhead.Row, nhead.Col)
		g.World[nhead.Row][nhead.Col] = model.NewSnakeBody() // 添加蛇头
		g.Snake.Body = append([]int{index}, g.Snake.Body...) // 添加蛇头，保留蛇尾
		g.Snake.Dict[index] = nhead
		// 待下次移动移除
		g.lastFood = &nhead
		// 随机一个食物 TODO
		g.randObject(1, 0)
		return
	}
	// 撞到障碍物
	if obj.IsObstacle() {
		g.IsDead = true
		fmt.Println("撞障碍物死了！！！")
		return
	}
	// 撞到自己了
	if obj.IsSnake() {
		g.IsDead = true
		fmt.Println("撞到自己死了！！！")
		return
	}
}

// 排除地图上已占用的格子，随机食物和障碍物
func (g *Game) randObject(foodNum, obstacleNum int) {
	// 空格子的 地图索引
	emptyCells := make([]int, 0)
	for row, cols := range g.World {
		for col, cell := range cols {
			if cell == nil {
				index := utils.Pos2Index(g.Set.Width, row, col)
				emptyCells = append(emptyCells, index)
			}
		}
	}
	// log.Println("所有空格子：", emptyCells)
	// 随机 foodNum+obstacleNum 个空格子
	indexs := make(map[int]int, 0) // cellMapIndex=格子的地图索引，sliceIndex=索引在当前 随机数切片的
	sliceIndex := 0
	for len(indexs) < foodNum+obstacleNum {
		randIndex := utils.Rand(len(emptyCells)) // 随机切片索引
		cellMapIndex := emptyCells[randIndex]    // 获取索引的 格子的地图索引
		if _, ok := indexs[cellMapIndex]; !ok {
			indexs[cellMapIndex] = sliceIndex
			sliceIndex++
		}
	}
	// log.Println("随机的格子：", indexs)
	// 放置到地图
	for mapIndex, sliceIndex := range indexs {
		row, col := utils.Index2Pos(g.Set.Width, mapIndex)
		if sliceIndex < foodNum { // 随机指定个数的食物放置到地图
			g.World[row][col] = model.NewFood(g.Set.FoodCategoryNum)
		} else { // 随机障碍物放置到地图
			g.World[row][col] = model.NewObstacle(g.Set.ObstacleCategoryNum)
		}
	}
}
